package pl.wroc.pwr.mss.server.util;

import java.io.File;
import java.io.FileInputStream;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;

import pl.wroc.pwr.mss.logic.domain.Song;
import pl.wroc.pwr.mss.server.exceptions.PlaybackException;

/**
 * Klasa do odtwarzania utworow mp3 znajdujacych sie na dysku twardym.
 * 
 * @author mjagus
 */
public class SongPlayback
{
    /**
     * Utwor z ktorego brana jest sciezka audio.
     */
    private Song song;

    /**
     * Obiekt pakietu JavaSound zawierajacy sciezke audio.
     */
    private Clip clip;

    /**
     * Tworzy odtwarzacz dla danego utworu.
     * 
     * @param song Dany utwor
     * @throws PlaybackException gdy nie mozna utworzyc obiektu Clip
     */
    public SongPlayback(Song song) throws PlaybackException
    {
        this.song = song;
        try {
            DataLine.Info info = new DataLine.Info(Clip.class, null);
            this.clip = (Clip)AudioSystem.getLine(info);
        } catch(LineUnavailableException e) {
            throw new PlaybackException("No line available", e);
        }
    }

    /**
     * Odtwarza utwor od poczatku (tworzy wtedy nowy strumien audio) lub od
     * momentu ostatniego spauzowania.
     * 
     * @throws PlaybackException Wyrzucany tylko podczas pierwszego odtworzenia
     * jesli utworzenie strumienia audio nie jest mozliwe
     */
    public void play() throws PlaybackException
    {
        if(!clip.isOpen())
        {
            try {
                FileInputStream fis = new FileInputStream(new File(song.getFilePath()));
                AudioInputStream ais = AudioSystem.getAudioInputStream(fis);
                AudioFormat baseFormat = ais.getFormat();
                AudioFormat decodedFormat = new AudioFormat(
                        AudioFormat.Encoding.PCM_SIGNED,
                        baseFormat.getSampleRate(),
                        16,
                        baseFormat.getChannels(),
                        baseFormat.getChannels() * 2,
                        baseFormat.getSampleRate(),
                        false);

                ais = AudioSystem.getAudioInputStream(decodedFormat, ais);
                clip.open(ais);
            } catch(Exception e) {
                throw new PlaybackException("Stream could not be created", e);
            }
        }

        clip.start();
    }

    /**
     * Pauzuje odtwarzanie utworu.
     */
    public void pause()
    {
        clip.stop();
    }

    /**
     * Zwraca czas odtwarzania utworu w milisekundach.
     * 
     * @return Czas odtwarzania utworu w milisekundach
     */
    public long getTime()
    {
        return clip.getMicrosecondPosition() / 1000;
    }

    /**
     * Ustawia czas odtwarzania utworu.
     * 
     * @param miliseconds Czas od ktorego ma byc odtwarzany utwor (podany w
     * milisekundach)
     */
    public void setTime(long miliseconds)
    {
        clip.setMicrosecondPosition(miliseconds * 1000);
    }

    /**
     * Zamyka odtwarzacz i zwalnia zasoby które przetrzymuje.
     */
    public void close()
    {
        pause();
        clip.close();
    }
}
